﻿using System;
using System.Collections.Concurrent;
using System.Configuration;
using Less;
using Less.Core;
using Less.Core.Model;
using Less.DAL;
/// <summary>
/// Base class to derive configuration classes
/// </summary>
public static class Config
{
    private static ApplicationStatus _applicationStatus = Status.ApplicationStatus; // Note: Forces create an applicationStatus Instance
    private static ConcurrentDictionary<string, object> _configValues;


    public static string ApplicationName
    {
        get
        {
            string name = ConfigurationManager.AppSettings["Config.ApplicationName"];
            if (name == null) name = AppDomain.CurrentDomain.FriendlyName.Trim().Replace(":", "");
            return name;
        }
    }

    public static string WorkDirectory
    {
        get
        {
            string name = ConfigurationManager.AppSettings["Config.WorkDirectory"];
            if (name == null) 
                name = Environment.GetFolderPath(folder: Environment.SpecialFolder.MyDocuments) + "\\Work\\";
            else
                name = string.Format(name, System.Environment.UserName);
            return name;
        }
    }

    public static string AppDirectory
    {
        get
        {
            string name = ConfigurationManager.AppSettings["Config.AppDirectory"];
            if (name == null) name = AppDomain.CurrentDomain.BaseDirectory;
            return name;
        }
    }

    public static string EmailAddress
    {
        get
        {
            string name = ConfigurationManager.AppSettings["Config.EmailAddress"];
            if (name == null) name =  ApplicationName + "@hotmail.com";
            return name;
        }
    }

    public static string SmtpPassword
    {
        get
        {
            string name = ConfigurationManager.AppSettings["Config.SmtpPassword"];
            if (name == null) name = "password";
            return name;
        }
    }

    public static string SmtpAddress
    {
        get
        {
            string name = ConfigurationManager.AppSettings["Config.SmtpAddress"];
            if (name == null) name = "smtp.live.com";
            return name;
        }
    }

    public static int SmtpPortAddress
    {
        get
        {
            string name = ConfigurationManager.AppSettings["Config.SmtpPortAddress"];
            if (name == null) name = "25";
            return int.Parse(name);
        }
    }



    public static bool CanUseDatabase { get; set; }

    /// <summary>
    /// Cache with key value pais of settings
    /// </summary>
    public static ConcurrentDictionary<string, object> ConfigValues
    {
        get
        {
            if (_configValues == null)
            {
                _configValues = new ConcurrentDictionary<string, object>();
            }
            return _configValues;
        }
        set { _configValues = value; }
    }

    /// <summary>
    /// Helper to get a value from a key
    /// Uses an dictionary cache for speed
    /// </summary>
    /// <typeparam name="T">Type of the expected value</typeparam>
    /// <param name="key">Name of the key to search</param>
    /// <param name="defaultValue">Provide a default value when not found</param>
    /// <param name="usesDatabase">By default all settigns will be stored at de database. Only core settings goes to file</param>
    /// <param name="culture"></param>
    /// <returns>The value associated with the key</returns>
    public static T GetValue<T>(string key, T defaultValue = default(T), bool usesDatabase = true, string culture = "")
    {
        T value = default(T);
        Type type = typeof(T);
        try
        {
            object valueReaded = null;
            string keyCulture = key + "." + culture;
            if (ConfigValues.ContainsKey(keyCulture))
            {
                valueReaded = ConfigValues[keyCulture];
                if (valueReaded != null)
                {
                    value = valueReaded.ConvertTo<T>();
                }
            }
            if ((type.IsClass && value == null) || (type.IsValueType && value.Equals(default(T))))
            {
                valueReaded = ReadValue(key, culture, usesDatabase);
                if (valueReaded != null)
                {
                    value = valueReaded.ConvertTo<T>();
                }
                if ((type.IsClass && value == null) || (type.IsValueType && value.Equals(default(T))))
                {
                    value = defaultValue;
                }
                if (valueReaded == null)
                {
                    SetValue(key, value, usesDatabase, culture, true); // Writes value because is new
                }
                else
                {
                    SetValue(key, value, usesDatabase, culture, false); // Only updates memory
                }
            }
        }
        catch (Exception e)
        {
            value = defaultValue;
            var cfgE = new Exception("Key:" + key, e);
            Status.AddMessage(new LogEntry() { Message = cfgE.ToLogString(), Category=EntryCategories.Config, Severity=EntrySeverities.Error });
        }
        return value;
    }

    /// <summary>
    /// Assings a value to a key
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <param name="usesDatabase"></param>
    /// <param name="culture"></param>
    /// <param name="writeValue"></param>
    public static void SetValue<T>(string key, T value, bool usesDatabase = true, string culture = "", bool writeValue = true)
    {
        string keyCulture = key + "." + culture;
        if (writeValue)
        {
            if (WriteValue(key, culture, value, usesDatabase))
            {
                ConfigValues.AddOrUpdate(keyCulture, value, OnUpdateValueFactory);
            }
            else
            {
                // Dont set value to force rewrite;
            }
        }
        else
        {
            ConfigValues.AddOrUpdate(keyCulture, value, OnUpdateValueFactory);
        }
    }
    private static object OnUpdateValueFactory(string keyCulture, object value)
    {
        return value;
    }

    /// <summary>
    /// Helper to read values from the storage
    /// </summary>
    /// <param name="key">The key name</param>
    /// <param name="usesDatabase">True for read from the database</param>
    /// <param name="culture"></param>
    /// <returns>The readed value or null if not found</returns>
    private static object ReadValue(string key, string culture, bool usesDatabase = true)
    {
        object value = null;
        try
        {
            if (usesDatabase && CanUseDatabase)
            {
                var dal = new ConfigSettingsDAL();
                value = dal.ReadValue(key, culture);
            }
            else
            {
                value = ConfigurationAppFileManager.ReadValue(key);
            }
        }
        catch (Exception ex)
        {
            var e = new Exception(String.Format("Reading code:{0} Culture:{1}", key, culture), ex);
            Status.AddMessage(new LogEntry() { Message = e.ToLogString(), Category = EntryCategories.Config, Severity = EntrySeverities.Error });
        }
        return value;
    }
    /// <summary>
    /// Helper to write to the Storage
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <param name="culture"></param>
    /// <param name="value"></param>
    /// <param name="usesDatabase"></param>
    private static bool WriteValue<T>(string key, string culture, T value, bool usesDatabase = true)
    {
        bool ok = true;
        try
        {
            if (usesDatabase)
            {
                if (CanUseDatabase)
                {
                    var dal = new ConfigSettingsDAL();
                    dal.WriteValue(key, value, culture);
                }
                else
                {
                    ok = false;
                }
            }
        }
        catch (Exception ex)
        {
            ok = false;
            var e = new Exception(String.Format("Writting code:{0} value:{1} ", key, value), ex);
            Status.AddMessage(new LogEntry() { Message = e.ToLogString(), Category = EntryCategories.Config, Severity = EntrySeverities.Error });
        }
        return ok;
    }
}

/// <summary>
/// An utility  to read settings from the deafult .config file
/// </summary>
public static class ConfigurationAppFileManager
{
    /// <summary>
    /// Helper to read values from .config files
    /// </summary>
    /// <param name="key">The key name</param>
    /// <returns>The readed value or null if not found</returns>
    public static object ReadValue(string key)
    {
        object value = null;
        value = ConfigurationManager.AppSettings[key];
        return value;
    }
}